﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.UI;
using NetDon.Utilities;
using SD.LLBLGen.Pro.ORMSupportClasses;


namespace NetDon.OA.Business
{
    public abstract class HierarchyCachableService<T> : CachableServiceBase<T>
        where T : class, IHierarchyData
    {
        protected HierarchyCachableService(string cacheKey)
            : base(cacheKey)
        {
        }

        private string GenerateChildrenCacheKey(object parentKey)
        {
            return CacheKey + "_" + parentKey + "_ChildrenKeys";
        }

        protected IEnumerable<T> GetChildNodes(object parentKey)
        {
            return GetChildNodes(parentKey, null);
        }

        protected virtual IEnumerable<T> GetChildNodes(object parentKey, ITransaction transaction)
        {
            if (parentKey == null)
            {
                throw new ArgumentNullException("parentKey");
            }
            string childrenCacheKey = GenerateChildrenCacheKey(parentKey);
            var keys = GetCache<ArrayList>(childrenCacheKey);
            if (keys == null)
            {
                IEnumerable<T> allData = OnLoadChildNodes(parentKey, transaction);
                if (allData == null)
                {
                    SetCache(childrenCacheKey, null);
                    return null;
                }
                keys = new ArrayList();
                foreach (T data in allData)
                {
                    object key = GetDataKey(data);
                    SetCache(GenerateCacheKey(key), data);
                    keys.Add(key);
                }
                SetCache(childrenCacheKey, keys);
                return allData;
            }
            var listData = new List<T>();
            foreach (object key in keys)
            {
                listData.Add(GetData(key, transaction));
            }
            return listData;
        }

        protected virtual T GetParentNode(T child)
        {
            return GetParentNode(child, null);
        }

        protected virtual T GetParentNode(T child, ITransaction transaction)
        {
            return GetData(GetParentKey(child), transaction);
        }

        protected abstract object GetParentKey(T data);

        protected abstract IEnumerable<T> OnLoadChildNodes(object parentKey, ITransaction transaction);

        protected override void OnAddDataComplete(object key, T data)
        {
            base.OnAddDataComplete(key, data);
            AddToParentCache(data);
        }

        private void RemoveFromParentCache(T data)
        {
            var parentKey = GetParentKey(data);
            if (MiscUtilities.IsNull(parentKey))
            {
                return;
            }
            string childrenCacheKey = GenerateChildrenCacheKey(parentKey);
            var keys = GetCache<ArrayList>(childrenCacheKey);
            if (keys == null)
            {
                return;
            }
            var key = GetDataKey(data);
            if (keys.Contains(key))
            {
                keys.Remove(key);
            }
            SetCache(childrenCacheKey, keys);
        }

        private void AddToParentCache(T data)
        {
            var parentKey = GetParentKey(data);
            if (MiscUtilities.IsNull(parentKey))
            {
                return;
            }
            string childrenCacheKey = GenerateChildrenCacheKey(parentKey);
            var keys = GetCache<ArrayList>(childrenCacheKey);
            if (keys == null)
            {
                return;
            }
            var key = GetDataKey(data);
            if (!keys.Contains(key))
            {
                keys.Add(key);
            }
            SetCache(childrenCacheKey, keys);
        }

        protected override void OnRemoveDataComplete(object key, T data)
        {
            base.OnRemoveDataComplete(key, data);
            RemoveFromParentCache(data);
        }

        protected override bool OnRemoveData(object key, T data, ITransaction transaction)
        {
            if(base.OnRemoveData(key, data, transaction))
            {
                var childNodes = GetChildNodes(key);
                if (childNodes!=null)
                {
                    foreach (var childNode in childNodes)
                    {
                        RemoveData(GetDataKey(childNode), transaction);
                    }
                }
                return true;
            }
            return false;
        }

        protected override bool ModifyData(object key, T data, ITransaction transaction)
        {
            var oldData = GetData(key, transaction);
            if (oldData == null)
            {
                return false;
            }
            if (base.ModifyData(key, data, transaction))
            {
                if (!Equals(GetParentKey(oldData), GetParentKey(data)))
                {
                    RemoveFromParentCache(oldData);
                    AddToParentCache(data);
                }
                return true;
            }
            return false;
        }
    }
}